package com.zwps.biz.domain.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zwps.biz.domain.converter.DictConverter;
import com.zwps.biz.domain.service.DictService;
import com.zwps.biz.domain.tool.TreeCodeTool;
import com.zwps.biz.api.model.dto.dict.AddDictDTO;
import com.zwps.biz.api.model.dto.dict.SelectDictTreeDTO;
import com.zwps.biz.api.model.dto.dict.UpdateDictDTO;
import com.zwps.biz.api.model.vo.dict.DictTreeVO;
import com.zwps.biz.dal.db.dao.DictDAO;
import com.zwps.biz.dal.db.object.DictDO;
import com.zwps.common.core.exception.UserActionException;
import com.zwps.common.core.service.CommonService;
import com.zwps.common.tool.tree.TreeTool;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

/**
 * @author shanwen
 * @description:
 * @date 2023-09-05
 * @ClassName DictServiceImpl
 */
@Slf4j
@Service
@AllArgsConstructor
public class DictServiceImpl implements DictService, CommonService {

    private DictDAO dictDAO;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addDict(AddDictDTO dto) {

        DictDO sortLastDict = null;
        String parentTreeCode = null;
        if (StringUtils.isNotBlank(dto.getParentId())) {
            DictDO dictDO = dictDAO.selectById(dto.getParentId());
            sortLastDict = findLastDict(dto.getParentId());
            parentTreeCode = dictDO.getTreeCode();
        } else {
            sortLastDict = findLastDict("");
        }

        String treeCode = TreeCodeTool.generateIncremental(parentTreeCode,
                sortLastDict == null ? null : sortLastDict.getTreeCode());

        DictDO dict = DictConverter.INSTANCE.toDictDO(dto);
        dict.setTreeCode(treeCode);
        if (dictDAO.insert(dict) < 1) {
            throw new UserActionException("新增字典失败");
        }
    }

    public DictDO findLastDict(String parentId) {
        LambdaQueryWrapper<DictDO> countDeptQuery = Wrappers.lambdaQuery(DictDO.class);
        if (StrUtil.isEmpty(parentId)) {
            countDeptQuery.eq(DictDO::getParentId, parentId);
            countDeptQuery.or().isNull(DictDO::getParentId);
        } else {
            countDeptQuery.eq(DictDO::getParentId, parentId);
        }
        countDeptQuery.last(" ORDER BY replace(tree_code,'" + TreeCodeTool.TREE_CODE_TAG + "', '')+0 desc limit 1");
        return dictDAO.selectOne(countDeptQuery);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateDict(String id, UpdateDictDTO dto) {
        DictDO selectdDict = dictDAO.selectById(id);
        if (selectdDict == null) {
            throw new UserActionException("当前修改的字典不存在");
        }

        if (selectdDict.getDictType() == 1) {
            throw new UserActionException("不能修改系统类型字典");
        }

        LambdaUpdateWrapper<DictDO> updateCurrentDict = Wrappers.lambdaUpdate(DictDO.class);
        String treeCode = null;
        DictDO sortLastDict = null;

        if (change(dto.getParentId(), selectdDict.getParentId())) {
            String parentTreeCode = "";

            if (!"".equals(dto.getParentId())) {
                if (dto.getParentId().equals(selectdDict.getId())) {
                    throw new UserActionException("不能选择自己作为上级部门");
                }

                DictDO selectParentDict = dictDAO.selectById(dto.getParentId());
                if (selectParentDict == null) {
                    throw new UserActionException("更新后的上级部门不存在");
                }

                if (selectParentDict.getTreeCode().startsWith(selectdDict.getTreeCode())) {
                    throw new UserActionException("不能选择子集部门作为上级部门");
                }
                parentTreeCode = selectParentDict.getTreeCode();
                sortLastDict = findLastDict(dto.getParentId());
                updateCurrentDict.set(DictDO::getParentId, dto.getParentId());
            } else {
                parentTreeCode = null;
            }

            treeCode = TreeCodeTool.generateIncremental(parentTreeCode,
                    sortLastDict == null ? null : sortLastDict.getTreeCode());

            updateCurrentDict.set(DictDO::getTreeCode, treeCode);
        }

        updateCurrentDict.set(change(dto.getDictName(), selectdDict.getDictName()), DictDO::getDictName,
                dto.getDictName());
        updateCurrentDict.set(change(dto.getDisplaySort(), selectdDict.getDisplaySort()), DictDO::getDisplaySort, dto.getDisplaySort());
        updateCurrentDict.set(change(dto.getDictCode(), selectdDict.getDictCode()), DictDO::getDictCode, dto.getDictCode());

        if (StrUtil.isNotEmpty(updateCurrentDict.getSqlSet())) {
            updateCurrentDict.eq(DictDO::getId, id);
            if (dictDAO.update(null, updateCurrentDict) < 1) {
                throw new UserActionException("修改字典信息失败");
            }
        }

        // 这里修改要在当前部门修改之后，如果当前部门的上级ID是置空，可能会出现treeCode违反唯一约束
        if (treeCode != null) {
            // 可能不存在子集，所以不作修改条数判断
            dictDAO.updateChildrenTreeCode(selectdDict.getTreeCode(), treeCode, TreeCodeTool.TREE_CODE_TAG);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteDicts(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));

        LambdaQueryWrapper<DictDO> deptQuery = Wrappers.lambdaQuery(DictDO.class).in(DictDO::getId, idList);
        List<DictDO> deleteDeptList = dictDAO.selectList(deptQuery);
        if (deleteDeptList.isEmpty()) {
            return;
        }
        for (DictDO dictDO : deleteDeptList) {
            if (dictDO.getDictType() == 1) {
                throw new UserActionException("不能删除系统类型字典");
            }
        }

        List<DictDO> deptList = dictDAO.selectList(Wrappers.lambdaQuery(DictDO.class).in(DictDO::getParentId, idList));
        for (DictDO dictDO : deptList) {
            throw new UserActionException(String.format("存在下级字典【%s】，请先删除下级字典", dictDO.getDictName()));
        }

        int result = dictDAO.deleteBatchIds(Arrays.asList(ids.split(",")));
        if (result != idList.size()) {
            long deptNum = dictDAO.selectCount(Wrappers.lambdaQuery(DictDO.class).in(DictDO::getId, idList));
            if (deptNum > 0) {
                log.error("Delete dicts id[{}] fail, select item {}, delete success {}", ids, idList.size(), result);
                throw new UserActionException("删除失败");
            }
        }

    }

    @Override
    public List<DictTreeVO> findDictTree(SelectDictTreeDTO dto) {
        LambdaQueryWrapper<DictDO> dictQuery = Wrappers.lambdaQuery(DictDO.class);
        if (StrUtil.isNotEmpty(dto.getParentId())) {
            DictDO dict = dictDAO.selectById(dto.getParentId());
            if (dict == null) {
                throw new UserActionException("查询指定的字典不存在");
            }
            dictQuery.and((a -> {
                a.eq(DictDO::getParentId, dto.getParentId()).or().likeRight(DictDO::getTreeCode, dict.getTreeCode());
            }));
        }
        dictQuery.like(StrUtil.isNotEmpty(dto.getDictName()), DictDO::getDictName, dto.getDictName());
        dictQuery.like(StrUtil.isNotEmpty(dto.getDictCode()), DictDO::getDictCode, dto.getDictCode());
        dictQuery.eq(dto.getDictType() != null, DictDO::getDictType, dto.getDictType());
        List<DictDO> dictList = dictDAO.selectList(dictQuery);
        if (dictList.isEmpty()) {
            return CollUtil.newArrayList();
        }
        Function<DictDO, DictTreeVO> treeInstance = (a) -> {
            DictTreeVO vo = DictConverter.INSTANCE.toDictTreeVO(a);
            return vo;
        };
        return TreeTool.build(dictList, treeInstance, (a) -> a.getId(), (a) -> a.getParentId(), (a) -> a.getDictName(),
                (a) -> a.getDisplaySort());
    }

    @Override
    public List<DictTreeVO> findDictByPid(String parentId) {
        LambdaQueryWrapper<DictDO> wrapper = new QueryWrapper<DictDO>().lambda();
        wrapper.eq(StringUtils.isNotBlank(parentId), DictDO::getParentId,parentId);
        List<DictDO> list = dictDAO.selectList(wrapper);
        ArrayList<DictTreeVO> voArrayList = new ArrayList<>();
        for (DictDO dictDO : list) {
            DictTreeVO vo = DictConverter.INSTANCE.toDictTreeVO(dictDO);
            voArrayList.add(vo);
        }
        return voArrayList;
    }
}
